home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-08-16 | 10.9 KB | 331 lines | [TEXT/MPS ] |
- {------------------------------------------------------------------------------
- #
- # Apple Macintosh Developer Technical Support
- #
- # Hierarchical Menu Example Application
- #
- # HierMenus
- #
- # HierMenus.p - Pascal Source
- #
- # Copyright © 1988 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions: 1.0 8/88
- #
- # Components: HierMenus.p August 1, 1988
- # HierMenus.r August 1, 1988
- # HierMenus.make August 1, 1988
- #
- # This program is a simple example of how to use hierarchical menus
- # in your application.
- #
- # See Sample and TESample for the general structure and MultiFinder
- # techniques that we recommend that you use when building a new application.
- #
-
- Hierarchical Menu Example
-
- This program is a simple example of how to add hierarchical menus to your
- application. A few tips:
-
- The toughest part is defining the menu item that "owns" the submenu. The
- item must be defined with a command-key of $1B, and a "mark" character
- whose value is the ID of the submenu. The submenu is defined just like
- a normal menu, but is inserted in the Menu Manager's private "hierarchical"
- menu list by specifying -1 as the second parameter to InsertMenu.
-
- It's easiest to declare all your menus (and submenus) as resources (as
- opposed to creating them at runtime with NewMenu), even if you're creating
- a submenu that will initially contain no items (like the Font submenu in
- this example). This simplifies your program's handling of submenus (note
- that in this example, since the submenus' IDs follow the regular menu's
- ID, the only special handling of submenus is the test (in SetUpMenus) to
- decide whether to use 0 or -1 as the second parameter to InsertMenu!).
-
- Submenus owned by applications' menus' IDs should always be in the range
- 1 through 235 (inclusive); desk accessories should use IDs 236 through
- 255, and should leave their menus and submenus in the menu bar only when
- they're active (that is, call InsertMenu when handling an activate event,
- and DeleteMenu when handling a deactivate event). This really applies to
- all menus; it's most important for pop-up and submenus, however. All this
- to prevent conflicts (it may be helpful to note that in searching through
- the menu list, the hierarchical list [containing all submenus & pop-up
- menus] is searched first).
-
- This particular example is meant to show the mechanics of creating and
- handling events from submenus; in the interest of simplicity, certain nice
- features (like checkmarking the current font and styles, setting the
- style of "real" font sizes in the Font submenu to "outline", etc) have been
- omitted.
-
- Have fun.
-
- Note: the current version does not actually call SysEnvirons to
- determine whether System 4.1 is running. This will be fixed, but
- in the meantime, run this with System
- *************************************************************************}
-
- PROGRAM HierMenus;
- {*
- * Hierarchical Menu Example
- * Bryan Stearns 05May87
- *
- * (C) 1987 Apple Computer, Inc.
- * All Rights Reserved.
- *}
-
- USES MemTypes, Quickdraw, OSIntf, ToolIntf, PackIntf;
-
- {$R-} {no range checking}
- {$D+} {Generate debug symbols}
-
- CONST
- menuBase = 128; {we number our menus starting at 128}
-
- mainMenu = menuBase; {main menu ID}
- {we don't care about the Font/Style/Size item numbers; we}
- {deal with choices in the submenus directly}
- quitItem = 5; {Quit item number}
-
- {the Font submenu ID}
- fontMenu = mainMenu+1;
- {we retrieve the font name from the menu by item number,}
- {then use GetFNum(the font name) to convert to family}
- {number. The font Name should be stored with the document,}
- {not the family number (because Font/DA Mover renumbers)}
-
- {the Style submenu ID}
- styleMenu = fontMenu+1;
- {Styles are dealt with in the same order they appear in}
- {the Quickdraw "Style" type. Sneaky, but consistent with}
- {other Macintosh applications}
-
- {the Size submenu ID}
- sizeMenu = styleMenu+1; {the Size submenu ID}
- {Sizes are retrieved as text from the menu item; this would}
- {allows advanced users to edit the menu to add custom sizes}
-
- lastMenu = sizeMenu; {the last menu resource}
- firstSubMenu = fontMenu; {the first submenu - see SetUpMenus}
-
- myWINDid = 128; {our window template}
- myALRTid = 128; {our “need right machine & sys software” alert}
- mySTRListID = 128; {our string list}
-
- VAR myWindow: WindowPtr; {our window pointer}
- myEvent: EventRecord; {Event record for GetNextEvent}
- myMenus: ARRAY [menuBase..lastMenu] OF MenuHandle; {our menus}
- done: BOOLEAN; {a flag that the user chose Quit}
- dragRect: Rect; {drag limit rectangle, for DragWindow}
-
- demoStr1, demoStr2, {Strings to draw in our demo window}
- demoStr3, demoStr4: Str255;
-
- curFont: INTEGER; {the current font number}
- curSize: INTEGER;
- curStyle: Style;
-
- i: INTEGER; {a temp}
- mResult: LongInt; {temp, result from MenuSelect}
-
-
- {*
- * Once-only initialization for my menus
- * (Thanks, Andy!)
- *}
- PROCEDURE SetUpMenus;
- VAR i: INTEGER;
- BEGIN
- {Get and insert each menu. The normal menus are inserted using zero}
- {as the second parameter to InsertMenu; the submenus get inserted using}
- {-1, to tell the Menu Manager to insert in the hierarchical menu list}
- FOR i := menuBase TO lastMenu DO BEGIN
- myMenus[i] := GetMenu(i); {get the menu from the resource}
-
- {insert it into a menu bar, one way or another}
- IF i < firstSubMenu THEN
- {it's a regular menu-bar menu}
- InsertMenu(myMenus[i],0) {insert at end of normal menu list}
- ELSE
- {it's a submenu}
- InsertMenu(myMenus[i],-1); {insert hierarchical submenu}
- END; {for each menu}
-
- {Add the fonts to the Font submenu}
- AddResMenu(myMenus[fontMenu],'FONT'); {add fonts to the font menu}
-
- DrawMenuBar; {draw our menus' titles}
- END; {setupmenus}
-
-
- {*
- * Handle a command, either from a command key or from
- * a menu choice. MResult is the value returned by
- * MenuSelect or MenuKey.
- *}
- PROCEDURE DoCommand (mResult: LONGINT);
- VAR theItem,theMenu: INTEGER;
- tmpStr: Str255;
- tmpLong: LongInt;
- BEGIN
- theItem := LoWord(mResult); {extract the menu and item numbers}
- theMenu := HiWord(mResult);
- CASE theMenu OF {which menu?}
- mainMenu: CASE theItem OF
- {Note that we don't have any cases for the font, style, or}
- {size menus here: those are handled independently, below. Only}
- {normal items that appear in this menu are taken care of here.}
- quitItem: done := TRUE;
- END; {mainMenu case}
-
- fontMenu: BEGIN
- {Here, we just change a global font number. However, if your}
- {application saves font information in its documents, you should}
- {save the font name (rather than the number), to avoid remapping}
- {because of Font/DA Mover number changes. Each time you open}
- {such a document, use GetFNum to find the right "current number".}
- GetItem(myMenus[fontMenu],theItem,tmpStr); {get the chosen font’s name}
- GetFNum(tmpStr,curFont); {get the new font’s number, to our global}
- InvalRect(thePort^.portRect); {remember that we need to redraw}
- END; {fontMenu case}
-
- styleMenu: BEGIN
- {The styles (other than Plain) are arranged in the}
- {menu in the same order that they appear in the}
- {Style type. If it's not plain, we can convert the}
- {item number to the proper enumerated value for the style}
- {and use set math to toggle the particular style}
- {characteristic.}
- IF theItem = 1 THEN curStyle := [] ELSE BEGIN {not 'Plain'}
- {it's not plain, so toggle this style characteristic}
- IF StyleItem(theItem-2) IN curStyle THEN
- {this style characteristic is currently on, so turn it off}
- curStyle := curStyle - [StyleItem(theItem-2)]
- ELSE
- {this style characteristic is currently on, so turn it off}
- curStyle := curStyle + [StyleItem(theItem-2)];
- END;
- InvalRect(thePort^.portRect); {remember that we need to redraw}
- END; {styleMenu case}
-
- sizeMenu: BEGIN
- {Read the size out of the menu item}
- GetItem(myMenus[sizeMenu],theItem,tmpStr); {get the chosen string}
- StringToNum(tmpStr,tmpLong); {convert to longint}
- curSize := tmpLong; {convert it to integer}
- InvalRect(thePort^.portRect); {remember that we need to redraw}
- END; {sizeMenu case}
-
- END; {menu case}
- IF NOT done THEN HiliteMenu(0); {un-hilight the chosen menu item}
- END; {DoCommand}
-
-
- {*
- * Main event loop
- *}
- PROCEDURE MainEventLoop;
- VAR thePart, theHeight: INTEGER;
- whichWindow: WindowPtr;
- theChar: CHAR;
- dragRect: Rect;
- fInfo: FontInfo;
- BEGIN
- done := FALSE; {we're not through yet!}
- dragRect := screenBits.bounds;
-
- {Main event loop}
- REPEAT
- SystemTask; {let the desk accs run}
- IF GetNextEvent(everyEvent,myEvent) THEN WITH myEvent DO BEGIN
- CASE what OF
- mouseDown: BEGIN {"Click"}
- thePart := FindWindow(where,whichWindow);
- CASE thePart OF
- inSysWindow: SystemClick(myEvent,whichWindow);
- inMenuBar: DoCommand(MenuSelect(myEvent.where)); {nothing special here!}
- inDrag: DragWindow(whichWindow,myEvent.where,dragRect);
- END; {case}
- END; {mouseDown}
-
- keyDown, autoKey: BEGIN
- theChar := CHR(BitAnd(myEvent.message,charCodeMask)); {get the char}
- IF BitAnd(myEvent.modifiers,cmdKey) <> 0 THEN BEGIN
- DoCommand(MenuKey(theChar)); {pass it to the command handler}
- END ELSE BEGIN {not a command key}
- {ignore it}
- END; {if not command key}
- END; {keydown}
-
- updateEvt: BEGIN {our window needs drawing}
- BeginUpdate(Windowptr(message));
-
- SetPort(myWindow); {make sure we're in our port}
- EraseRect(thePort^.portRect); {erase the old stuff}
-
- TextFont(curFont); {Set the right font/size/style}
- TextSize(curSize);
- TextFace(curStyle);
-
- GetFontInfo(fInfo);
- WITH fInfo DO theHeight := ascent+descent+leading;
- MoveTo(20,20+theHeight); DrawString(demoStr1);
- MoveTo(20,20+(theHeight*2)); DrawString(demoStr2);
- MoveTo(20,20+(theHeight*3)); DrawString(demoStr3);
- MoveTo(20,20+(theHeight*5)); DrawString(demoStr4);
-
- EndUpdate(WindowPtr(message));
- END;
- END; {case}
- END; {if we got an event}
-
- UNTIL done; {keep it up until the user quits}
-
- END; {maineventloop}
-
-
- {*
- * Main
- *}
- BEGIN
- {Initialize all the usual managers}
- InitGraf(@thePort);
- InitFonts;
- FlushEvents(everyEvent,0);
- InitWindows;
- InitMenus;
- TEInit;
- InitDialogs(NIL);
- InitCursor;
-
- {Check to make sure we’re running on a machine}
- {capable of supporting hierarchical menus; clearly,}
- {we must do this before calling SetUpMenus!}
- (** SysEnvirons(xxx); **)
- IF FALSE THEN BEGIN {Sorry, see your dealer}
- i := StopAlert(myALRTid,NIL); {put up the alert}
- ExitToShell; {back to the finder}
- END;
-
- curFont := 1; {Application font}
- curSize := 0; {default size}
- curStyle := []; {plain}
-
- SetUpMenus; {the oldest Macintosh subroutine!}
-
- {read the strings that we draw in our window}
- {from a STR# resource}
- GetIndString(demoStr1,mySTRListID,1);
- GetIndString(demoStr2,mySTRListID,2);
- GetIndString(demoStr3,mySTRListID,3);
- GetIndString(demoStr4,mySTRListID,4);
-
- {Get our window}
- myWindow := GetNewWindow(myWINDid,NIL,POINTER(-1));
-
- MainEventLoop; {handle events until the user quits}
- END.
-
-
-